import logging
import redis
from flask import Flask, render_template, g, current_app
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect
from flask_wtf.csrf import generate_csrf

"""info包中处理所有的网站业务逻辑"""
from info.utils.common import get_user_info
from config import config

# TODO 创建db和redis_db数据库,从create_app中抽取出来
db = SQLAlchemy()
redis_db = None  # type:redis.StrictRedis


def create_app(config_name):
    """传入不同的配置名字,初始化其对应配置的app对象"""

    # 5.配置项目日志
    setup_log(config_name)
    app = Flask(__name__)

    # TODO 导入配置信息
    app.config.from_object(config[config_name])

    # 1.创建mysql对象并与app关联
    db.init_app(app)

    # 2.创建redis(NoSQL数据库)的对象,并填入配置信息
    global redis_db
    redis_db = redis.StrictRedis(host=config[config_name].REDIS_HOST, port=config[config_name].REDIS_PORT,
                                 decode_responses=True)

    # 3.CSRFProtect只做验证工作,cookie中的csrf_token和表单中的csrf_token需要自己设置
    # CSRFProtect实现原理:提取前端传来的csrf_token和session中的csrf_token进行比对
    """
    def protect(self):
        if request.method not in current_app.config['WTF_CSRF_METHODS']:
            return

        try:
            validate_csrf(self._get_csrf_token())
        except ValidationError as e:
            logger.info(e.args[0])
            self._error_response(e.args[0])

        if request.is_secure and current_app.config['WTF_CSRF_SSL_STRICT']:
            if not request.referrer:
                self._error_response('The referrer header is missing.')

            good_referrer = 'https://{0}/'.format(request.host)

            if not same_origin(request.referrer, good_referrer):
                self._error_response('The referrer does not match the host.')

        g.csrf_valid = True  # mark this request as CSRF valid
    """
    # 默认校验的请求方式:'WTF_CSRF_METHODS', ['POST', 'PUT', 'PATCH', 'DELETE']
    # 默认校验接受的参数名:'WTF_CSRF_HEADERS', ['X-CSRFToken', 'X-CSRF-Token']
    CSRFProtect(app)

    # 3.Session关联app
    Session(app)

    # 4.1 TODO 通过勾子函数after_request生成csrf_token的值,并写入cookie中
    # 示例:Cookie: session=cc659e59-ca10-4caf-92fe-62ea0076f52a.y39nWOWMFNrU_HOb5ZTGiww-6Ng;
    # csrf_token=ImIyMTY4YTA4OWRkYzMzZTE0Y2E0MjRiYTEzZjgzNGNmMjA3MTJiYTEi.XO6mNg.Oujddk1qST14x1PvWxvmCa06F5I
    @app.after_request
    def after_request(response):
        """每次收到浏览器请求后,在响应中设置cookie:scrf_token,以做下次请求验证"""
        # 调用函数生成csrf_token的值
        csrf_token = generate_csrf()
        # 通过cookie传给前端
        response.set_cookie('csrf_token', csrf_token)
        return response

    # 4.2 TODO 统一处理404页面
    @app.errorhandler(404)
    @get_user_info
    def page_not_found(e):
        current_app.logger.error(e)
        user = g.user
        data = {'user_info': user.to_dict() if user else None}
        return render_template('news/404.html', data=data)

    # 8.注册自定义过滤器
    from info.utils.common import click_news_filter
    app.add_template_filter(click_news_filter)

    # 6.注册蓝图index_blu(主页模块)到app中
    from info.modules.index import index_blu
    app.register_blueprint(index_blu)

    # 6.1.注册蓝图passport_blu(验证模块)到app中
    from info.modules.passport import passport_blu
    app.register_blueprint(passport_blu)

    # 6.2.注册蓝图news_blu(验证模块)到app中
    from info.modules.news import news_blu
    app.register_blueprint(news_blu)

    # 6.3.注册蓝图profile_blu(个人中心模块)到app中
    from info.modules.profile import profile_blu
    app.register_blueprint(profile_blu)

    # 6.4.注册蓝图admin_blu(后台管理模块)到app中
    from info.modules.admin import admin_blu
    app.register_blueprint(admin_blu)

    return app


def setup_log(config_name):
    """配置日志"""
    from logging.handlers import RotatingFileHandler

    # 设置日志的记录等级
    logging.basicConfig(level=config[config_name].LOG_LEVEL)
    # 创建日志记录器，指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
    file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 100, backupCount=10)
    # 创建日志记录的格式:日志等级-日志信息的文件名-行数-日志信息
    formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
    # 日志记录器设置日志记录格式
    file_log_handler.setFormatter(formatter)
    # 为全局的日志工具对象（flask app使用的）添加日志记录器
    logging.getLogger().addHandler(file_log_handler)
